/*******************************************************************/
/* @ All Rights Reserved Copyright (C) 2014 broada,Co.,Ltd.        */
/*                                                                 */
/*  NAME      = frstat_module.c                                    */
/*  NOTE      =                                                    */
/*  DATE      = 2015/01/09 by guojq                                */
/*******************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <pthread.h>

#include "frcomm.h"
#include "frmem.h"
#include "frlog.h"
#include "frlist.h"
#include "frhash2.h"
#include "frmodule_mng.h"

#define  MODULE_STAT_HASH_SIZE   100
//统计信息是平均DELTA_SECOND秒的信息
#define  DELTA_SECOND   1

typedef struct __module_msg {
    unsigned long ul_requests;
    unsigned long ul_responses;
    unsigned long ul_fails;
    unsigned long ul_succs;
} module_msg; 

typedef struct __module_msg_avg {
    unsigned long ul_requests[4];
    unsigned long ul_responses[4];
    unsigned long ul_fails[4];
    unsigned long ul_succs[4];
} module_msg_avg; 

typedef struct __module_stat {
    char *p_module_name;
    u_short us_proto;
    int     i_l7_proto;
    struct timeval start_time; //模块开始时间
    
    module_msg st_stat_total;
    module_msg_avg st_stat_avg;
    
} module_stat;

FR_STATIC Frhash_header g_st_hash_module;
FR_STATIC pthread_rwlock_t g_module_stat_rwlock;
FR_STATIC int g_i_timeout_module = 1;
FR_STATIC int g_i_flag_init_module = FR_FAIL;

/*******************************************************************************
*  FUNC     :  释放节点内容
*  ARGS     :  p_node 
*  RTN      :
*  NOTE     :  
*******************************************************************************/
FR_STATIC void __module_node_free(Frlist_node* p_node)
{
    module_stat *p_stat_node = NULL;
    
    if (p_node && p_node->data)
    {
        p_stat_node = (module_stat *)p_node->data;
        if (p_stat_node->p_module_name)
            frfree(p_stat_node->p_module_name);
        frfree(p_stat_node);
    }
}

/*******************************************************************************
*  FUNC     :  组成哈希用key
*  ARGS     :  us_proto     (协议 tcp/udp)
*           :  i_l7_proto   (l7协议名)
            :  p_key        (保存地址)
            :  ui_key_size  (保存地址长度)
*  RTN      :  key长度
*  NOTE     :
*******************************************************************************/
FR_STATIC int __frhash_make_key(u_short us_proto, int i_l7_proto
                                , char *p_key, unsigned int ui_key_size)
{
    int i_idx=0;
    int *stupid_strict_aliasing_warnings=(int*)p_key;

    if(p_key == NULL || ui_key_size <= sizeof(int) + sizeof(u_short))
    {
        FRLOG_ERROR_DETAIL("expect hash key err");
        return 0;
    }
    *stupid_strict_aliasing_warnings = us_proto;
    i_idx += sizeof(u_short);
    *(int *) (p_key + i_idx) = i_l7_proto;
    i_idx += sizeof(int);
    
    return i_idx;
}

/*******************************************************************************
*  FUNC     :  增加module信息
*  ARGS     :  us_proto         模块协议
*           :  i_l7_proto       使用的l7协议名
*           :  i_requests       请求数
*           :  i_responses      应答数
*           :  i_succs          成功
*           :  i_fails          失败数
*  RTN      :  FR_SUCC   (成功)
*              FR_FAIL   (失败)
*  NOTE     :
*******************************************************************************/
int frstat_module_add(u_short us_proto, int i_l7_proto
               , int i_requests, int i_responses, int i_succs, int i_fails)
{
    module_stat *p_st_new_module_stat = NULL;
    Frlist_node *p_st_new_node = NULL;
    Frlist_node *p_st_node = NULL;
    char arr_hash_key[20] = {'\0'};
    int i_hash_key_len = 0;

    if(g_i_flag_init_module == FR_FAIL)
        return FR_FAIL;
    
    i_hash_key_len = __frhash_make_key(us_proto, i_l7_proto, arr_hash_key, sizeof(arr_hash_key));
    if (i_hash_key_len == 0)
        return FR_FAIL;
    
    pthread_rwlock_wrlock(&g_module_stat_rwlock);
    p_st_node = frhash2_getStr(&g_st_hash_module, arr_hash_key, i_hash_key_len);
    if (p_st_node && p_st_node->data)
    {
        module_stat *p_st_list_stat = (module_stat *)p_st_node->data;
        
        p_st_list_stat->st_stat_total.ul_requests += i_requests;
        p_st_list_stat->st_stat_total.ul_responses += i_responses;
        p_st_list_stat->st_stat_total.ul_succs += i_succs;
        p_st_list_stat->st_stat_total.ul_fails += i_fails;
        pthread_rwlock_unlock(&g_module_stat_rwlock);
        return FR_SUCC;
    }
    
    //不存在，添加到链表中
    p_st_new_module_stat = (module_stat *) frmalloc(sizeof(module_stat));
    if (p_st_new_module_stat == NULL)
    {
        pthread_rwlock_unlock(&g_module_stat_rwlock);
        return FR_FAIL;
    }
    
    memset(p_st_new_module_stat, 0x00, sizeof(module_stat));
    
    {
        Frmodule_mng *p_st_module = frmodule_get_module(i_l7_proto);
        if(p_st_module && p_st_module->p_st_l7_module && p_st_module->p_st_l7_module->p_module_name)
            p_st_new_module_stat->p_module_name = frstrdup(p_st_module->p_st_l7_module->p_module_name);
        else
            p_st_new_module_stat->p_module_name = frstrdup(" ");
    }

    p_st_new_module_stat->us_proto = us_proto;
    p_st_new_module_stat->i_l7_proto = i_l7_proto;
    
    p_st_new_module_stat->st_stat_total.ul_requests = i_requests;
    p_st_new_module_stat->st_stat_total.ul_responses = i_responses;
    p_st_new_module_stat->st_stat_total.ul_succs = i_succs;
    p_st_new_module_stat->st_stat_total.ul_fails = i_fails;

    gettimeofday(&p_st_new_module_stat->start_time, 0);
    
    p_st_new_node = frlist_node_create(p_st_new_module_stat);
    if (p_st_new_node == NULL)
    {
        pthread_rwlock_unlock(&g_module_stat_rwlock);
        frfree(p_st_new_module_stat);
        return FR_FAIL;
    }

    frhash2_addStr(&g_st_hash_module, arr_hash_key, i_hash_key_len, p_st_new_node);
    pthread_rwlock_unlock(&g_module_stat_rwlock);
    
    return FR_SUCC;
}

/*******************************************************************************
*  FUNC     :  删除module信息
*  ARGS     :  us_proto          模块协议
*           :  i_l7_proto       使用的l7协议名
*  RTN      :  FR_SUCC   (成功)
*              FR_FAIL   (失败)
*  NOTE     :
*******************************************************************************/
int frstat_module_del(u_short us_proto, int i_l7_proto)
{
    char arr_hash_key[20] = {'\0'};
    int i_hash_key_len = 0;

    if(g_i_flag_init_module == FR_FAIL)
        return FR_FAIL;
    
    i_hash_key_len = __frhash_make_key(us_proto, i_l7_proto, arr_hash_key, sizeof(arr_hash_key));
    if (i_hash_key_len == 0)
        return FR_FAIL;
    
    pthread_rwlock_wrlock(&g_module_stat_rwlock);
    frhash2_delStr(&g_st_hash_module, arr_hash_key, i_hash_key_len, __module_node_free);
    pthread_rwlock_unlock(&g_module_stat_rwlock);
    
    return FR_SUCC;
}

/*******************************************************************************
*  FUNC     :  计算平均值
*  ARGS     :  arr_ul_last          总数表
*           :  p_avg                算好的平均值
*  RTN      :
*  NOTE     :
*******************************************************************************/
FR_STATIC void __stat_module_count_avg(unsigned long arr_ul_last[4], unsigned long *p_avg)
{
    if (arr_ul_last[0] == 0 || arr_ul_last[1] == 0)
    {
        //记录的是总数，因此前2个节点为0的情况说明平均为0或者是刚开始统计，平均设成0
        *p_avg = 0;
        return;
    }
    if (arr_ul_last[2] == 0)
    {
        *p_avg = (arr_ul_last[0] - arr_ul_last[1])/g_i_timeout_module;
        return;
    }
    if (arr_ul_last[3] == 0)
    {
        *p_avg = (arr_ul_last[0] - arr_ul_last[2])/(g_i_timeout_module*2);
        return;
    }
    *p_avg = (arr_ul_last[0] - arr_ul_last[3])/(g_i_timeout_module*3);
    return;
}

/*******************************************************************************
*  FUNC     :  计算平均值
*  ARGS     :  p_node           当前节点
*           :  p_data           
*  RTN      :  1   (成功)
*  NOTE     :
*******************************************************************************/
FR_STATIC int __frmodule_bps(Frlist_node* p_node, const void *p_data)
{
    module_stat *p_st_list_stat = NULL;
    
    p_st_list_stat = (module_stat *)p_node->data;
    
    memmove(p_st_list_stat->st_stat_avg.ul_requests + 1, p_st_list_stat->st_stat_avg.ul_requests, sizeof(unsigned long)*3);
    memmove(p_st_list_stat->st_stat_avg.ul_responses + 1, p_st_list_stat->st_stat_avg.ul_responses, sizeof(unsigned long)*3);
    memmove(p_st_list_stat->st_stat_avg.ul_succs + 1, p_st_list_stat->st_stat_avg.ul_succs, sizeof(unsigned long)*3);
    memmove(p_st_list_stat->st_stat_avg.ul_fails + 1, p_st_list_stat->st_stat_avg.ul_fails, sizeof(unsigned long)*3);
    
    p_st_list_stat->st_stat_avg.ul_requests[0] = p_st_list_stat->st_stat_total.ul_requests;
    p_st_list_stat->st_stat_avg.ul_responses[0] = p_st_list_stat->st_stat_total.ul_responses;
    p_st_list_stat->st_stat_avg.ul_succs[0] = p_st_list_stat->st_stat_total.ul_succs;
    p_st_list_stat->st_stat_avg.ul_fails[0] = p_st_list_stat->st_stat_total.ul_fails;
    

#ifdef FRDBG_MODULE_STAT_SHOW
    {
        unsigned long ul_requests_avg = 0;
        unsigned long ul_responses_avg = 0;
        unsigned long ul_succs_avg = 0;
        unsigned long ul_fails_avg = 0;
        
        __stat_module_count_avg(p_st_list_stat->st_stat_avg.ul_requests, &ul_requests_avg);
        __stat_module_count_avg(p_st_list_stat->st_stat_avg.ul_responses, &ul_responses_avg);
        __stat_module_count_avg(p_st_list_stat->st_stat_avg.ul_succs, &ul_succs_avg);
        __stat_module_count_avg(p_st_list_stat->st_stat_avg.ul_fails, &ul_fails_avg);
            
        printf("l7_proto=%d\tbps time=%d\trequests=%lu\tresponses=%lu\tsuccs=%lu\tfails=%lu\n"
             , p_st_list_stat->i_l7_proto
             , i_delta_msec
             , ul_requests_avg, ul_responses_avg
             , ul_succs_avg, ul_fails_avg);
    }
#endif

    return 1;
}

/*******************************************************************************
*  FUNC     :  运行计算平均值
*  ARGS     :  
*  RTN      :  
*  NOTE     :
*******************************************************************************/
void frstat_module_bps(void)
{
    if(g_i_flag_init_module == FR_FAIL)
        return;
    
    pthread_rwlock_wrlock(&g_module_stat_rwlock);
    frhash2_run(&g_st_hash_module, NULL, __frmodule_bps);
    pthread_rwlock_unlock(&g_module_stat_rwlock);
    
    return;
}

/*******************************************************************************
*  FUNC     :  显示所有的平均值
*  ARGS     :  p_node           当前节点
*           :  p_outbuff        显示的内容
*           :  ui_outlen        显示的内容长度
*  RTN      :  
*  NOTE     :  p_outbuff需要外面分配内存
*******************************************************************************/
FR_STATIC void __frmodule_show(Frlist_node* p_node, char *p_outbuff, unsigned int ui_outlen)
{
    module_stat *p_st_list_stat = NULL;
    unsigned long ul_requests_avg = 0;
    unsigned long ul_responses_avg = 0;
    unsigned long ul_succs_avg = 0;
    unsigned long ul_fails_avg = 0;
    int i_is_stub = FR_NO;
    struct tm *p_tm = NULL;
    
    p_st_list_stat = (module_stat *)p_node->data;
    
    __stat_module_count_avg(p_st_list_stat->st_stat_avg.ul_requests, &ul_requests_avg);
    __stat_module_count_avg(p_st_list_stat->st_stat_avg.ul_responses, &ul_responses_avg);
    __stat_module_count_avg(p_st_list_stat->st_stat_avg.ul_succs, &ul_succs_avg);
    __stat_module_count_avg(p_st_list_stat->st_stat_avg.ul_fails, &ul_fails_avg);

    p_tm = localtime(&p_st_list_stat->start_time.tv_sec);

    //取得模块状态
    {
        Frmodule_mng *p_st_module = frmodule_get_module(p_st_list_stat->i_l7_proto);
        if(p_st_module)
            i_is_stub = p_st_module->i_is_stub;
    }

    snprintf(p_outbuff, ui_outlen
            , "module_name=%s, proto=%s(%u), l7=%d, load_time=%04d/%02d/%02d %02d:%02d:%02d, status=%s\n\trequests=%lu(%lu/s), responses=%lu(%lu/s), fails=%lu(%lu/s), succs=%lu(%lu/s)\n"
            , p_st_list_stat->p_module_name, FR_L4PROTO2STR(p_st_list_stat->us_proto), p_st_list_stat->us_proto, p_st_list_stat->i_l7_proto
            , p_tm->tm_year + 1900,p_tm->tm_mon + 1,p_tm->tm_mday
            , p_tm->tm_hour,p_tm->tm_min,p_tm->tm_sec
            , i_is_stub==FR_YES?"USE_STUB":"USE_MODULE"
            , p_st_list_stat->st_stat_total.ul_requests, ul_requests_avg
            , p_st_list_stat->st_stat_total.ul_responses, ul_responses_avg
            , p_st_list_stat->st_stat_total.ul_fails, ul_fails_avg
            , p_st_list_stat->st_stat_total.ul_succs, ul_succs_avg);
    
    return;
}

/*******************************************************************************
*  FUNC     :  显示模块所有的平均值 (建议做三次平均后再做output)
*  ARGS     :  pp_result            模块的所有信息
*  RTN      :  
*  NOTE     :  pp_result需要外面释放
*******************************************************************************/
void frstat_module_output(char **pp_result)
{
    char *p_outbuff=NULL;

    if(g_i_flag_init_module == FR_FAIL)
        return ;

    pthread_rwlock_rdlock(&g_module_stat_rwlock);
    frhash2_show_all2(&g_st_hash_module, &p_outbuff, __frmodule_show);
    pthread_rwlock_unlock(&g_module_stat_rwlock);
    
    if(pp_result == NULL)
    {
        frdbg("==========================\n");
        printf("output stat\n%s\n", p_outbuff);
        frdbg("==========================\n");
        frfree(p_outbuff);
    }
    else
        *pp_result = p_outbuff;
    
    return;
}

/*******************************************************************************
*  FUNC     :  取得当前模块的request response succ fail的平均值
*  ARGS     :  i_l7_proto         模块名
*           :  p_ul_requests_all  总请求
*           :  p_ul_responses_all 总应答
*           :  p_ul_succs_all     总成功
*           :  p_ul_fails_all     总失败
*           :  p_ul_requests_avg  平均请求
*           :  p_ul_responses_avg 平均应答
*           :  p_ul_succs_avg     平均成功
*           :  p_ul_fails_avg     平均失败
*  RTN      :  FR_SUCC   (成功)
*              FR_FAIL   (失败)
*  NOTE     :  IPPROTO_TCP和IPPROTO_UDP的总信息
*******************************************************************************/
int frstat_module_get_info(int i_l7_proto
                         , unsigned long *p_ul_requests_all, unsigned long *p_ul_responses_all
                         , unsigned long *p_ul_succs_all, unsigned long *p_ul_fails_all
                         , unsigned long *p_ul_requests_avg, unsigned long *p_ul_responses_avg
                         , unsigned long *p_ul_succs_avg, unsigned long *p_ul_fails_avg)
{
    Frlist_node *p_st_node = NULL;
    u_short us_proto = 0;
    char arr_hash_key[20] = {'\0'};
    int i_hash_key_len = 0;
    unsigned long ul_requests_tmp = 0;
    unsigned long ul_responses_tmp = 0;
    unsigned long ul_succs_tmp = 0;
    unsigned long ul_fails_tmp = 0;

    unsigned long ul_requests_all = 0;
    unsigned long ul_responses_all = 0;
    unsigned long ul_succs_all = 0;
    unsigned long ul_fails_all = 0;

    unsigned long ul_requests_avg = 0;
    unsigned long ul_responses_avg = 0;
    unsigned long ul_succs_avg = 0;
    unsigned long ul_fails_avg = 0;

    if(g_i_flag_init_module == FR_FAIL)
        return FR_FAIL;

    //IPPROTO_TCP搜索
    us_proto = IPPROTO_TCP;
    i_hash_key_len = __frhash_make_key(us_proto, i_l7_proto, arr_hash_key, sizeof(arr_hash_key));
    if (i_hash_key_len == 0)
        return FR_FAIL;

    pthread_rwlock_rdlock(&g_module_stat_rwlock);
    p_st_node = frhash2_getStr(&g_st_hash_module, arr_hash_key, i_hash_key_len);
    if (p_st_node && p_st_node->data)
    {
        module_stat *p_st_list_stat = (module_stat *)p_st_node->data;

        __stat_module_count_avg(p_st_list_stat->st_stat_avg.ul_requests, &ul_requests_tmp);
        __stat_module_count_avg(p_st_list_stat->st_stat_avg.ul_responses, &ul_responses_tmp);
        __stat_module_count_avg(p_st_list_stat->st_stat_avg.ul_succs, &ul_succs_tmp);
        __stat_module_count_avg(p_st_list_stat->st_stat_avg.ul_fails, &ul_fails_tmp);
        ul_requests_avg += ul_requests_tmp;
        ul_responses_avg += ul_responses_tmp;
        ul_succs_avg += ul_succs_tmp;
        ul_fails_avg += ul_fails_tmp;

        ul_requests_all += p_st_list_stat->st_stat_total.ul_requests;
        ul_responses_all += p_st_list_stat->st_stat_total.ul_responses;
        ul_succs_all += p_st_list_stat->st_stat_total.ul_succs;
        ul_fails_all += p_st_list_stat->st_stat_total.ul_fails;
    }

    //IPPROTO_UDP搜索
    us_proto = IPPROTO_UDP;
    i_hash_key_len = __frhash_make_key(us_proto, i_l7_proto, arr_hash_key, sizeof(arr_hash_key));
    if (i_hash_key_len == 0)
    {
        pthread_rwlock_unlock(&g_module_stat_rwlock);
        return FR_FAIL;
    }

    p_st_node = frhash2_getStr(&g_st_hash_module, arr_hash_key, i_hash_key_len);
    if (p_st_node && p_st_node->data)
    {
        module_stat *p_st_list_stat = (module_stat *)p_st_node->data;

        __stat_module_count_avg(p_st_list_stat->st_stat_avg.ul_requests, &ul_requests_tmp);
        __stat_module_count_avg(p_st_list_stat->st_stat_avg.ul_responses, &ul_responses_tmp);
        __stat_module_count_avg(p_st_list_stat->st_stat_avg.ul_succs, &ul_succs_tmp);
        __stat_module_count_avg(p_st_list_stat->st_stat_avg.ul_fails, &ul_fails_tmp);
        ul_requests_avg += ul_requests_tmp;
        ul_responses_avg += ul_responses_tmp;
        ul_succs_avg += ul_succs_tmp;
        ul_fails_avg += ul_fails_tmp;

        ul_requests_all += p_st_list_stat->st_stat_total.ul_requests;
        ul_responses_all += p_st_list_stat->st_stat_total.ul_responses;
        ul_succs_all += p_st_list_stat->st_stat_total.ul_succs;
        ul_fails_all += p_st_list_stat->st_stat_total.ul_fails;
    }
    pthread_rwlock_unlock(&g_module_stat_rwlock);

    *p_ul_requests_all = ul_requests_all;
    *p_ul_responses_all = ul_responses_all;
    *p_ul_succs_all = ul_succs_all;
    *p_ul_fails_all = ul_fails_all;

    *p_ul_requests_avg = ul_requests_avg;
    *p_ul_responses_avg = ul_responses_avg;
    *p_ul_succs_avg = ul_succs_avg;
    *p_ul_fails_avg = ul_fails_avg;

    return FR_SUCC;
}

/*******************************************************************************
*  FUNC     :  初始化模块信息
*  ARGS     :  i_timeout        每次做平均的间隔时间
*  RTN      :  FR_SUCC   (成功)
*              FR_FAIL   (失败)
*  NOTE     :  调用bps,show,add都需要先运行过init (只运行一次)
*******************************************************************************/
int frstat_module_init(int i_timeout)
{
    if (pthread_rwlock_init(&g_module_stat_rwlock, NULL) != 0)
    {
        FRLOG_ERROR_DETAIL("pthread_rwlock_init failed");
        return FR_FAIL;
    }
    
    if (frhash2_create(&g_st_hash_module, MODULE_STAT_HASH_SIZE) != FR_SUCC)
    {
        FRLOG_ERROR_DETAIL("frhash2_create failed");
        return FR_FAIL;
    }

    g_i_timeout_module = i_timeout;
    g_i_flag_init_module = FR_SUCC;
    return FR_SUCC;
}

/*******************************************************************************
*  FUNC     :  退出模块信息
*  ARGS     :  
*  RTN      :  
*  NOTE     :  调用完init后，使用完调用exit  (只能运行一次)
*******************************************************************************/
void frstat_module_destory(void)
{
    if(g_i_flag_init_module == FR_FAIL)
        return;

    pthread_rwlock_wrlock(&g_module_stat_rwlock);
    frhash2_free(&g_st_hash_module, __module_node_free);
    pthread_rwlock_unlock(&g_module_stat_rwlock);
    
    pthread_rwlock_destroy(&g_module_stat_rwlock);

    g_i_flag_init_module = FR_FAIL;
    return;
}
